﻿using System;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using AmaroK86.MassEffect3;
using AmaroK86.MassEffect3.DLC;
using Gibbed.MassEffect3.FileFormats;

namespace AmaroK86.MassEffect3.DLCExplorer2
{
    public partial class DLCExplorer2 : Form
    {
        enum controlList
        {
            listView,
            treeView,
            nothing
        }

        controlList lastFocused;
        DLCHandler dlcBase;

        public DLCExplorer2()
        {
            InitializeComponent();
        }

        #region auxfunctions

        private long Getsize(string filename)
        {
            FileInfo fInfo = new FileInfo(filename);
            return fInfo.Length;
        }

        private void checkSave()
        {
            if (dlcBase == null)
                return;

            buttonSave.Enabled = dlcBase.bUnchanged ? false : true;
        }

        private TreeNode AddNode(TreeNode node, string key)
        {
            if (node.Nodes.ContainsKey(key))
            {
                return node.Nodes[key];
            }
            else
            {
                return node.Nodes.Add(key, key, 0);
            }
        }

        private string getFolderPath(TreeNode folder)
        {
            string folderPath = "/";
            while (folder.Parent != null)
            {
                folderPath = "/" + folder.Name + folderPath;
                folder = folder.Parent;
            }
            return folderPath;
        }

        private bool isFolder(ListViewItem listItem)
        {
            return listItem.ImageIndex == 0; // imageindex 0 has a folder icon, so it's a folder :P
        }

        private bool isFile(ListViewItem listItem)
        {
            return !isFolder(listItem);
        }

        private void addItem(sfarFile entry, ListView listView)
        {
            string[] listItemEntry = new string[4];
            listItemEntry[0] = Path.GetFileName(entry.fileName);
            listItemEntry[1] = Path.GetExtension(entry.fileName).Substring(1).ToUpper() + " file";
            if (entry.fileStatus.Key == action.add || entry.fileStatus.Key == action.replace)
            {
                listItemEntry[2] = Getsize(entry.fileStatus.Value) + " bytes";
                listItemEntry[3] = "not yet defined bytes";
            }
            else
            {
                listItemEntry[2] = entry.uncompressedSize + " bytes";
                listItemEntry[3] = entry.compressedSize + " bytes";
            }

            int imageIndex;
            switch (Path.GetExtension(entry.fileName))
            {
                case ".tlk": imageIndex = 2; break;
                case ".afc": imageIndex = 4; break;
                case ".bik": imageIndex = 5; break;
                default: imageIndex = 1; break;
            }
            ListViewItem listItem = new ListViewItem(listItemEntry, imageIndex);
            listItem.Name = entry.fileName;

            switch (entry.fileStatus.Key)
            {
                case action.delete: listItem.BackColor = Color.Red; break;
                case action.replace: listItem.BackColor = Color.Yellow; break;
                case action.add: listItem.BackColor = Color.LightGreen; break;
            }

            listView.Items.Add(listItem);
        }

        private void replaceItem(sfarFile entry, ListView listView)
        {
            switch (lastFocused)
            {
                case controlList.listView:
                    {
                        foreach (ListViewItem listItem in listView.SelectedItems)
                        {
                            if (dlcBase.fileList[FileNameHash.Compute(listItem.Name)].fileStatus.Key == action.add)
                                continue;

                            if (isFile(listItem))
                            {
                                dlcBase.undo(listItem.Name);
                            }
                            listItem.BackColor = Color.Empty;
                            listItem.Selected = false;
                        }

                        buttonUndo.Enabled = false;
                    }
                    break;
            }

            // if nothing is changed then there's no need to save
            checkSave();
        }

        #endregion

        private void openSfarFile(string fileName)
        {
            dlcBase = new DLCHandler(fileName);

            TreeNode root = new TreeNode("ME3 DLC");
            root.Name = fileName;
            TreeNode node = root;
            treeView.Nodes.Clear();
            treeView.Nodes.Add(root);

            foreach (sfarFile entry in dlcBase.fileList)
            {
                if (entry.fileName != null)
                {
                    string filePath = entry.fileName;
                    node = root;
                    string[] pathBits = filePath.Substring(1).Split('/');
                    for (int i = 0; i < pathBits.Length - 1; i++)
                    {
                        node = AddNode(node, pathBits[i]);
                    }
                }
            }
            treeView.SelectedNode = root;
            treeView.ExpandAll();

            buttonSave.Visible = true;
            buttonSave.Enabled = false;
            buttonAdd.Visible = true;
            buttonDelete.Visible = true;
            buttonExtract.Visible = true;
            buttonUndo.Visible = true;
            buttonUndo.Enabled = false;
            buttonInfo.Visible = true;

            panelSearch.Visible = true;

            listView.Items.Clear();
            showFolder("/");
        }

        private void showFolder(string folderPath)
        {
            List<string> folders =
                dlcBase.fileList.
                Where(dlcFile =>
                    dlcFile.fileName != null &&
                    dlcFile.fileName.Length >= folderPath.Length &&
                    dlcFile.fileName.Substring(0, folderPath.Length) == folderPath &&
                    dlcFile.fileName.Substring(folderPath.Length).Split('/').Count() > 1).
                Select(dlcFile => dlcFile.fileName.Substring(0, folderPath.Length) + dlcFile.fileName.Substring(folderPath.Length).Split('/')[0]).
                Distinct().
                ToList();

            List<sfarFile> folderList =
                dlcBase.fileList.
                Where(dlcFile =>
                    dlcFile.fileName != null &&
                    dlcFile.fileName.Length >= folderPath.Length &&
                    dlcFile.fileName.Substring(0, folderPath.Length) == folderPath &&
                    dlcFile.fileName.Substring(folderPath.Length).Split('/').Count() == 1).
                OrderBy(dlcFile => Path.GetFileName(dlcFile.fileName)).
                ToList();

            listView.BeginUpdate();
            listView.Items.Clear();
            foreach (string folderName in folders)
            {
                listView.Items.Add(folderName + "/", Path.GetFileName(folderName), 0);
            }

            foreach (sfarFile entry in folderList)
            {
                addItem(entry, listView);
            }
            listView.EndUpdate();

            toolStripStatusLabel.Text = listView.Items.Count + " element" + ((listView.Items.Count != 1) ? "s" : "");
        }

        private void add(object sender, EventArgs e)
        {
            openFileDialog.Filter = "All files|*.*";
            openFileDialog.Multiselect = true;

            if (openFileDialog.ShowDialog() != DialogResult.OK)
                return;

            string dlcFolderName = getFolderPath(treeView.SelectedNode);

            foreach (string fileName in openFileDialog.FileNames)
            {
                string newAddFileName = dlcFolderName + Path.GetFileName(fileName);
                if (dlcBase.fileList.Contains(FileNameHash.Compute(newAddFileName)))
                {
                    if (dlcBase.fileList[FileNameHash.Compute(newAddFileName)].fileStatus.Key == action.add)
                    {
                        MessageBox.Show("Warning, file " + Path.GetFileName(fileName) + " already added, skipping action", "File already added", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                        continue;
                    }

                    if (DialogResult.Yes == MessageBox.Show("Warning, file " + Path.GetFileName(fileName) + " already exist, do you want to replace it?", "File already exist", MessageBoxButtons.YesNo, MessageBoxIcon.Warning))
                    {
                        dlcBase.replaceFile(newAddFileName, fileName);
                        listView.Items[newAddFileName].BackColor = Color.Yellow;
                    }
                }
                else
                {
                    dlcBase.addFile(fileName, dlcFolderName);
                    sfarFile entry = dlcBase.fileList[FileNameHash.Compute(newAddFileName)];
                    addItem(entry, listView);
                }
            }

            checkSave();
        }

        private void delete(object sender, EventArgs e)
        {
            switch (lastFocused)
            {
                case controlList.listView:
                    {
                        foreach (ListViewItem listItem in listView.SelectedItems)
                        {
                            if (isFile(listItem))
                            {
                                if (dlcBase.fileList[FileNameHash.Compute(listItem.Name)].fileStatus.Key == action.add)
                                    listItem.Remove();

                                dlcBase.deleteFile(listItem.Name);
                            }
                            else
                            {
                                dlcBase.deleteFolder(listItem.Name);
                            }
                            listItem.BackColor = Color.Red;
                            listItem.Selected = false;
                        }
                    }
                    break;
                case controlList.treeView:
                    {
                        dlcBase.deleteFolder(getFolderPath(treeView.SelectedNode));

                        listView.BeginUpdate();
                        foreach (ListViewItem listItem in listView.Items)
                        {
                            listItem.BackColor = Color.Red;
                        }
                        listView.EndUpdate();

                        buttonSave.Enabled = true;
                    }
                    break;
            }

            checkSave();
            buttonUndo.Enabled = false;
        }

        private void undo(object sender, EventArgs e)
        {
            switch (lastFocused)
            {
                case controlList.listView:
                    {
                        foreach (ListViewItem listItem in listView.SelectedItems)
                        {
                            if (dlcBase.fileList[FileNameHash.Compute(listItem.Name)].fileStatus.Key == action.add)
                                listItem.Remove();

                            if (isFile(listItem))
                            {
                                dlcBase.undo(listItem.Name);
                            }
                            listItem.BackColor = Color.Empty;
                            listItem.Selected = false;
                        }

                        buttonUndo.Enabled = false;
                    }
                    break;
            }

            // if nothing is changed then there's no need to save
            checkSave();
        }

        private void showProperties(object sender, EventArgs e)
        {
            switch (lastFocused)
            {
                case controlList.listView:
                    {
                        foreach (ListViewItem listItem in listView.SelectedItems)
                        {
                            if (isFile(listItem))
                            {
                                sfarFile entry = dlcBase.fileList[FileNameHash.Compute(listItem.Name)];
                                generalProperties propForm = new generalProperties(entry);
                                propForm.Show();
                            }
                        }
                    }
                    break;
            }
        }

        #region Main Buttons

        private void buttonOpen_Click(object sender, EventArgs e)
        {
            if (openFileDialog.InitialDirectory == "")
                openFileDialog.InitialDirectory = ME3Paths.DLCPath;

            if (openFileDialog.FileName == "")
                openFileDialog.FileName = "Default.sfar";

            openFileDialog.Filter = "ME3 DLC|*.sfar|All files|*.*";
            openFileDialog.Multiselect = false;

            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                openSfarFile(openFileDialog.FileName);
            }

            folderBrowserDialog.SelectedPath = Path.GetDirectoryName(openFileDialog.FileName);
        }

        #endregion

        private void treeView_AfterSelect(object sender, TreeViewEventArgs e)
        {
            string folderPath = getFolderPath(treeView.SelectedNode);

            showFolder(folderPath);
        }

        private void listView_DoubleClick(object sender, EventArgs e)
        {
            if (listView.SelectedItems.Count == 0)
                return;

            // if a folder is double-clicked then opens it
            ListViewItem listItem = listView.SelectedItems[0];
            if (isFolder(listItem))
                showFolder(listItem.Name);
        }

        private void buttonHelp_Click(object sender, EventArgs e)
        {
            About aboutWindow = new About();
            aboutWindow.ShowDialog();
        }

        private void buttonExtract_Click(object sender, EventArgs e)
        {
            switch (lastFocused)
            {
                case controlList.listView:
                    {
                        if (listView.SelectedItems.Count == 0)
                            return;

                        if (folderBrowserDialog.ShowDialog() != DialogResult.OK)
                            return;

                        foreach (ListViewItem listItem in listView.SelectedItems)
                        {
                            if (isFile(listItem))
                            {
                                dlcBase.extractFile(listItem.Name, folderBrowserDialog.SelectedPath + "\\" + listItem.Text);
                            }
                            else
                            {
                                dlcBase.extractFolder(listItem.Name, folderBrowserDialog.SelectedPath);
                            }
                        }
                    }
                    break;
                case controlList.treeView:
                    {
                        if (folderBrowserDialog.ShowDialog() != DialogResult.OK)
                            return;

                        dlcBase.extractFolder(getFolderPath(treeView.SelectedNode), folderBrowserDialog.SelectedPath);
                    }
                    break;
            }
        }

        private void replaceToolStripMenuItem_Click(object sender, EventArgs e)
        {

        }

        private void listView_Enter(object sender, EventArgs e)
        {
            lastFocused = controlList.listView;
        }

        private void treeView_Enter(object sender, EventArgs e)
        {
            lastFocused = controlList.treeView;
        }

        private void listView_Click(object sender, EventArgs e)
        {
            if (listView.SelectedItems.Count == 0)
                buttonUndo.Enabled = false;

            // if selected only 1 item
            if (listView.SelectedItems.Count == 1)
            {
                if (isFile(listView.SelectedItems[0]))
                {
                    if (dlcBase.fileList[FileNameHash.Compute(listView.SelectedItems[0].Name)].fileStatus.Key != action.copy)
                        buttonUndo.Enabled = true;
                    else
                        buttonUndo.Enabled = false;
                }
            }
            else
            {
                foreach (ListViewItem listItem in listView.SelectedItems)
                {
                    if (isFile(listItem))
                    {
                        if (dlcBase.fileList[FileNameHash.Compute(listItem.Name)].fileStatus.Key != action.copy)
                            buttonUndo.Enabled = true;
                    }
                }
            }
        }

        private void listView_Leave(object sender, EventArgs e)
        {
            //buttonUndo.Enabled = false;
        }

        private void contextMenuStripListItem_Opening(object sender, CancelEventArgs e)
        {
            undoToolStripMenuItem.Visible = false;

            foreach (ListViewItem listItem in listView.SelectedItems)
            {
                if (isFile(listItem))
                {
                    if (dlcBase.fileList[FileNameHash.Compute(listItem.Name)].fileStatus.Key != action.copy)
                        undoToolStripMenuItem.Visible = true;
                }
            }
        }

        private void buttonSave_Click(object sender, EventArgs e)
        {
            new WindowProgressForm(dlcBase.commitChanges, null).ShowDialog();
            openSfarFile(dlcBase.sfarFileName);
        }
    }
}
